﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace Smark.Core
{
    
    public class ObjectEnter:IDisposable
    {
        private string mOwnerID;
        static ObjectEnter()
        {
            Core.Functions.Action(CheckExpires);
        }
        public ObjectEnter(string id)
        {
            mOwnerID = Guid.NewGuid().ToString("N");
            mID = id;
            Enter(mOwnerID, id, 10000);
        }
        public ObjectEnter(string id, int milliseconds)
        {
            mOwnerID = Guid.NewGuid().ToString("N");
            mID = id;
            Enter(mOwnerID, id, milliseconds);
        }
        private string mID = null;
        private static Dictionary<string, LockItem> mObjectTable = new Dictionary<string, LockItem>(100);
        public static void TryEnter(string id, Smark.Core.AsyncDelegate<string,bool> lockhandler)
        {
            TryEnter(id, 10000, 1000, lockhandler);
        }
        public static void TryEnter(string id, int milliseconds, int timeout, Smark.Core.AsyncDelegate<string,bool> lockhandler)
        {
            string owner = Guid.NewGuid().ToString("N");
            if (lockhandler != null)
                lockhandler(owner, TryEnter(owner, id, milliseconds, timeout));
            else
                TryEnter(owner, id, milliseconds, timeout);
            
        }
        public static bool TryEnter(string owner, string id, int milliseconds, int timeout)
        {
            
            Log.Write<ObjectEnter>(Log.LogType.Track, string.Format("TryEnter Resource Owner:{0}\tResource:{1}\tSeconds:{2}\tTimeOut:{3}", owner, id, milliseconds, timeout));
            QueueState state = new QueueState();
            state.EntrerMilliseconds = milliseconds;
            state.TimeOut = timeout;
            if (!GetLockItem(id).Enter(owner, milliseconds, state))
            {
                state.Handler.Reset();
                state.Handler.WaitOne(state.TimeOut);
                return GetLockItem(id).Enter(owner, milliseconds, null);  
            }
            else
            {
                return true;
            }

        }
        public static void Enter(string owner,string id, int milliseconds)
        {
            Log.Write<ObjectEnter>(Log.LogType.Track, string.Format("Enter Resource Owner:{0}\tResource:{1}\tSeconds:{2}", owner, id, milliseconds));
           
            QueueState state = new QueueState();
            state.EntrerMilliseconds = milliseconds;
            if (!GetLockItem(id).Enter(owner, milliseconds, state))
            {
                state.Handler.Reset();
                state.Handler.WaitOne();                
            }
            
        }
        public static void Exit(string owner,string id)
        {
            Log.Write<ObjectEnter>(Log.LogType.Track, string.Format("Exit Resource Owner:{0}\tResource:{1}", owner, id));
        
            if (mObjectTable.ContainsKey(id))
                 mObjectTable[id].Exit(owner);
        
        }
        private static LockItem GetLockItem(string id)
        {
            lock (mObjectTable)
            {
                LockItem item = null;
                if (!mObjectTable.ContainsKey(id))
                {
                    item = new LockItem();
                    item.ID = id;
                    mObjectTable.Add(id, item);
                }
                else
                {
                    item = mObjectTable[id];
                }
                return item;
            }

        }
        private static void CheckExpires()
        {
            while (true)
            {
                lock (mObjectTable)
                {
                    foreach (System.Collections.Generic.KeyValuePair<string, LockItem> item in mObjectTable)
                    {
                        item.Value.Expires();
                    }
                }
                System.Threading.Thread.Sleep(5000);
            }
        }
        class LockItem
        {
            const string SYSTEM_ID = "SYSTEM_4EB49CA1-7470-44af-94DE-B2128B9B5BA9";
            public LockItem()
            {
                
            }
            public bool Enter(string owner, int milliseconds, QueueState handler)
            {
                
                lock (this)
                {
                    if (Count>0 && owner != Owner)
                    {
                      
                        if (handler != null)
                            EnterWait(handler);
                        return false;
                    }
                    Count++;
                    if (milliseconds > 120000)
                        milliseconds = 120000;
                    EntrerMilliseconds = milliseconds;
                    Owner = owner;
                    EnterTime = DateTime.Now;
                    return true;
                }
            }

            private void EnterWait(QueueState handler)
            {
                lock (mWaitHandle)
                {
                    mWaitHandle.Enqueue(handler);
                    
                }
          
            }
            private Queue<QueueState> mWaitHandle = new Queue<QueueState>();
           
            public void Exit(string owner)
            {
                lock (this)
                {
                    if (owner == SYSTEM_ID)
                    {
                        Owner = null;
                        Count = 0;
                        
                    }
                    else
                    {
                        if (owner == Owner)
                        {
                            Count--;
                        }
                    }
                    if (Count == 0)
                    {
                        lock (mWaitHandle)
                        {
                            if (mWaitHandle.Count > 0)
                            {
                                QueueState state = mWaitHandle.Dequeue();
                                state.Handler.Set();
                            }
                        }
                    }
                    
                 
                }
            }
			private int mEntrerMilliseconds;
            public int EntrerMilliseconds
            {
                get
				{
					return mEntrerMilliseconds;
				}
                set
				{
					mEntrerMilliseconds = value;
				}
            }
			private DateTime mEnterTime;
            public DateTime EnterTime
            {
                get
				{
					return mEnterTime;
				}
                set
				{
					mEnterTime= value;
				}
            }
			private string mID;
            public string ID
            {
                get
				{
					return mID;
				}
                set
				{
					mID = value;
				}
            }
			private string mOwner;
            public string Owner
            {
                get
				{
					return mOwner;
				}
                set
				{
					mOwner = value;
				}
            }
			private int mCount;
            public int Count
            {
                get
				{
					return mCount;
				}
                set
				{
					mCount = value;
				}
            }
            public void Expires()
            {
                lock (this)
                {
                    TimeSpan ts = DateTime.Now - EnterTime;
                    if (ts.TotalMilliseconds > EntrerMilliseconds)
                        Exit(SYSTEM_ID);
                }
            }
        }

        #region IDisposable 成员
        private bool mDisposed = false;
        public void Dispose()
        {
            lock (this)
            {
                if (!mDisposed)
                {
                    Exit(mOwnerID, mID);
                    mDisposed = true;
                }
            }
           
        }

        #endregion
    }
    class QueueState
    {
        public QueueState()
        {
            EntrerMilliseconds = 10000;
        }
		private int mEntrerMilliseconds;
        public int EntrerMilliseconds
        {
            get
			{
				return mEntrerMilliseconds;
			}
            set
			{
				mEntrerMilliseconds = value;
			}
        }
		private int mTimeOut;
        public int TimeOut
        {
            get
			{
				return mTimeOut;
			}
            set
			{
				mTimeOut = value;
			}
        }
        private System.Threading.EventWaitHandle mHandler = new EventWaitHandle(false, EventResetMode.ManualReset);
        public System.Threading.EventWaitHandle Handler
        {
            get
            {
                return mHandler;
            }
            
        }
    }
}
